/*
 *  Copyright 2008 The MITRE Corporation (http://www.mitre.org/). All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */


package org.mitre.lattice.graph;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Container;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.Rectangle;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.swing.BorderFactory;

import org.jgraph.JGraph;
import org.jgraph.graph.AbstractCellView;
import org.jgraph.graph.AttributeMap;
import org.jgraph.graph.ConnectionSet;
import org.jgraph.graph.DefaultGraphCell;
import org.jgraph.graph.GraphCell;
import org.jgraph.graph.GraphConstants;
import org.jgraph.layout.SugiyamaLayoutAlgorithm;
import org.jgraph.util.JGraphUtilities;
import org.mitre.lattice.lattice.LatticeFunctions;
import org.mitre.lattice.lattice.LatticeNode;
import org.mitre.lattice.util.Constants;
import org.mitre.mrald.util.Config;
import org.mitre.mrald.util.MraldError;
import org.mitre.mrald.util.User;

/**
 *  Class that contains any functions that deal with Graphing of the Lattice
 *  objects, using JGraph as a graphical interface.
 *
 *@author     jchoyt
 *@created    November 12, 2003
 */
public class GraphLattice extends javax.swing.JFrame
{

    public final static String KEY = GraphLattice.class.getName();
    public final static Logger log = Logger.getLogger( KEY );
    static {log.setLevel(Level.FINEST);}

    //static MraldGraph graph = null;

    static CustomizableGraph graph = null;

    Container contentPane;

    private static int screenWidth = 0;
    private static int screenHeight = 0;


    /**
     *  Constructor for the GraphLattice object
     */
    public GraphLattice()

    {
	log.entering(KEY, "init()");

        init();
    }


    /**
     *  Gets the graph attribute of the GraphLattice class
     *
     *@return    The graph value
     */
    public static JGraph getGraph()
    {
        log.entering(KEY, "getGraph()");
        return graph;
    }


    /**
     *  Description of the Method
     *
     *@param  toImage        Description of the Parameter
     *@param  name           Description of the Parameter
     *@param  showLattice    Description of the Parameter
     *@return                Description of the Return Value
     *@exception  Exception  Description of the Exception
     */
    public static String convertToHtml(Object toImage, String name, String showLattice, User user)
         throws Exception
    {
        java.awt.Image graphImage = ((Component) toImage).createImage(screenWidth, screenHeight);
        //java.awt.Image graphImage = ( ( Component ) toImage ).createImage( 800, 600 );
        java.awt.image.BufferedImage graphBImage = ImageConvertor.convertToBufferedImage(graphImage);

        java.io.FileOutputStream fos = new java.io.FileOutputStream(Config.getProperty("BasePath") + "/" + Config.getProperty("latticeGraph") + ".jsp");

        //"<%=Config.getProperty( \"CSS\" ) %>"
        String header = "<%@ page errorPage=\"ErrorHandler.jsp\" %>\n" +
            "<%@ page import=\"org.mitre.mrald.util.Config\" %>\n" +
        "<%@ page import=\"org.mitre.mrald.util.User\" %>\n" +
         "<%@ page import=\"org.mitre.lattice.graph.GraphSelect\" %>\n" +
        "<%User user = (User)pageContext.getSession().getAttribute(Config.getProperty(\"cookietag\"));" +
              "String graphName = GraphSelect.getLastGraph(user) + \".jpg\";" +
             " graphName = graphName.substring(graphName.indexOf(\"/\") + 1);%>" +
            "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\">\n" +
            "<HTML>\n<HEAD>\n " + Config.getProperty("CSS") +
            "<META HTTP-EQUIV=\"CACHE-CONTROL\" CONTENT = \"NO-CACHE\">" +
            "</HEAD>\n<BODY>\n" +
            "<TABLE SUMMARY=\"\" WIDTH=\"100%\" BORDER=\"0\" CELLSPACING=\"0\" CELLPADDING=\"0\">\n" +
            "<TR>\n<table width=\"100%\" border=\"0\" cellspacing=\"0\" cellpadding=\"0\">\n<tr>" +
            "<TD CLASS=\"bord\">\n<TABLE SUMMARY=\"\" WIDTH=\"100%\" BORDER=\"0\" CELLSPACING=\"1\" CELLPADDING=\"0\">\n" +
            "<TR><form ENCTYPE=\"x-www-form-urlencoded\" ACTION=\"../LatticePanel.jsp\" METHOD=\"POST\" target=\"groupList\">\n" +
            "<input type=\"hidden\" name=\"showLattice\" value=\"" + showLattice + "\">\n" +
            "<!--td width=\"12%\" class=\"nav\" onMouseOver=\"this.className='navhilite';\">Test</td-->\n" +
            "<TH><B><input name=\"ModifyNode\" value=\"Add\" type=\"submit\"></input></B></TH>\n" +
            "<TH><b><input name=\"ModifyNode\" value=\"Delete\" type=\"submit\"></input></b></th>\n" +
            "<TH><b><input name=\"ModifyNode\" value=\"Add Link\" type=\"submit\"></input></b></th>\n" +
            "<TH><b><input name=\"ModifyNode\" value=\"Delete Link\" type=\"submit\"></input></b></th>\n" +
            "<TH><b><input name=\"ModifyNode\" value=\"Focus\" type=\"submit\"></input></b></th>" +
            "<TH><b><input name=\"ModifyNode\" value=\"Unfocus\" type=\"submit\"></input></b></th>" +
            "</form><form ENCTYPE=\"x-www-form-urlencoded\" action=\"../latticePrefs.jsp\" METHOD=\"POST\"  target=\"groupList\"><TH><b>" +
            "<input name=\"Prefs\" value=\"Preferences\" type=\"submit\" ></input></b></th></form>" +
            "</TR>\n</TABLE>\n</TD>\n</TR>\n</TABLE>\n</TR>\n</TABLE>";

        // PM: selectNew doesn't seem to do anything.
//        GraphSelect selectNew = new GraphSelect();
        String newGraph = GraphSelect.getLastGraph(user);
//        selectNew.cleanUp();

        //As already in the graphs directory
        newGraph = newGraph.substring(newGraph.indexOf("/") + 1);
        //String imageTag = "<br><img src=\"" + newGraph + ".jpg\" width=\"" +
        //    graphBImage.getWidth() + "\" height=\"" + graphBImage.getHeight() + "\"" +
        //    " \border=\"0\" align=\"center\" ismap usemap=\"#map\"></form></body>";

    String imageTag = "<br><img src=\"<%=graphName%>\" width=\"" +
                    graphBImage.getWidth() + "\" height=\"" + graphBImage.getHeight() + "\"" +
                    " \border=\"0\" align=\"center\" ismap usemap=\"#map\"></form></body>";

        fos.write(header.getBytes());
        fos.write(imageTag.getBytes());
        fos.flush();
        fos.close();

        return header + imageTag;
    }


    /**
     *  Description of the Method
     *
     *@return                Description of the Return Value
     *@exception  Exception  Description of the Exception
     */
    public static String convertToHtml(User user)
         throws Exception
    {
        java.io.FileOutputStream fos = new java.io.FileOutputStream(Config.getProperty("BasePath") + "/" + Config.getProperty("latticeGraph") + ".jsp");


        //String header = "<%@ taglib uri=\"/WEB-INF/lattice.tld/\" prefix=\"lattice\"%>\n" +
          String header = "<%@ page errorPage=\"ErrorHandler.jsp\" %>\n" +
            "<%@ page import=\"org.mitre.mrald.util.Config\" %>\n" +
        "<%@ page import=\"org.mitre.mrald.util.User\" %>\n" +
         "<%@ page import=\"org.mitre.lattice.graph.GraphSelect\" %>\n" +
        "<%User user = (User)pageContext.getSession().getAttribute(Config.getProperty(\"cookietag\"));" +
              "String graphName = GraphSelect.getLastGraph(user) + \".jpg\";" +
             " graphName = graphName.substring(graphName.indexOf(\"/\") + 1);%>" +
            "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\">\n" +
            "<HTML>\n<HEAD>\n " + Config.getProperty("CSS") +
            "<META HTTP-EQUIV=\"CACHE-CONTROL\" CONTENT = \"NO-CACHE\">" +
            "</HEAD>\n<BODY>\n" +
            "<TABLE SUMMARY=\"\" WIDTH=\"100%\" BORDER=\"0\" CELLSPACING=\"0\" CELLPADDING=\"0\">\n" +
            "<TR>\n<table width=\"100%\" border=\"0\" cellspacing=\"0\" cellpadding=\"0\">\n<tr>" +
            "<TD CLASS=\"bord\">\n<TABLE SUMMARY=\"\" WIDTH=\"100%\" BORDER=\"0\" CELLSPACING=\"1\" CELLPADDING=\"0\">\n" +
            "<TR><form ENCTYPE=\"x-www-form-urlencoded\" ACTION=\"../LatticePanel.jsp\" METHOD=\"POST\" target=\"groupList\">\n" +
            "<input type=\"hidden\" name=\"showLattice\" value=\"true\">\n" +
            "<!--td width=\"12%\" class=\"nav\" onMouseOver=\"this.className='navhilite';\">Test</td-->\n" +
            "<TH><B><input name=\"ModifyNode\" value=\"Add\" type=\"submit\"></input></B></TH>\n" +
            "<TH><b><input name=\"ModifyNode\" value=\"Delete\" type=\"submit\"></input></b></th>\n" +
            "<TH><b><input name=\"ModifyNode\" value=\"Add Link\" type=\"submit\"></input></b></th>\n" +
            "<TH><b><input name=\"ModifyNode\" value=\"Delete Link\" type=\"submit\"></input></b></th>\n" +
            "<TH><b><input name=\"ModifyNode\" value=\"Focus\" type=\"submit\"></input></b></th>" +
            "<TH><b><input name=\"ModifyNode\" value=\"Unfocus\" type=\"submit\"></input></b></th>" +
            "</form><form ENCTYPE=\"x-www-form-urlencoded\" action=\"../latticePrefs.jsp\" METHOD=\"POST\"  target=\"groupList\"><TH><b>" +
            "<input name=\"Prefs\" value=\"Preferences\" type=\"submit\" ></input></b></th></form>" +
            "</TR>\n</TABLE>\n</TD>\n</TR>\n</TABLE>\n</TR>\n</TABLE>";

        // PM: selectNew doesn't seem to do anything.
//        GraphSelect selectNew = new GraphSelect();
        String newGraph = GraphSelect.getLastGraph(user);
//        selectNew.cleanUp();

        //As already in the graphs directory
        newGraph = newGraph.substring(newGraph.indexOf("/") + 1);
        //String imageTag = "<br><img src=\"" + newGraph + ".jpg\" width= " + screenWidth +
        //   " height=" + screenHeight + " border=\"0\" align=\"center\" ismap usemap=\"#map\"></form></body>";

    String imageTag = "<br><img src=\"<%=graphName%>\" width= " + screenWidth +
           " height=" + screenHeight + " border=\"0\" align=\"center\" ismap usemap=\"#map\"></form></body>";

        fos.write(header.getBytes());
        fos.write(imageTag.getBytes());
        fos.flush();
        fos.close();

        return header + imageTag;
    }


    /**
     *  Description of the Method
     *
     *@param  toImage        Description of the Parameter
     *@exception  Exception  Description of the Exception
     */
    public static void createImageFile(Object toImage, User user) throws Exception
    {
        log.entering(KEY, "createImageFile()");
        try
        {
            GraphSelect selectNew = new GraphSelect();

            String newGraph = selectNew.setNewGraph(user);
            //String tempStr = Config.getProperty("BasePath") + "/" +  Config.getProperty( "latticeGraph" );

            String tempStr = Config.getProperty("BasePath") + "/" + newGraph;

            File graphFile = new File(tempStr + ".jpg");
            //graphFile.delete();

            selectNew.cleanUp();
            log.finest("New graph picture info retrieved from database.");
            Image graphImage = ((Component) toImage).createImage(screenWidth, screenHeight);
            //java.awt.Image graphImage = ( ( Component ) toImage ).createImage( 800, 600 );
            if (graphImage == null)
            {
                throw new MraldError("Graph Image is null. Could not create graph. ");
            } else
            {
                BufferedImage graphBImage = ImageConvertor.convertToBufferedImage(graphImage);
                log.finest("Frame converted to BufferedImage.");
                if (graphBImage != null)
                {
                    Graphics g = graphBImage.getGraphics();

                    ((Component) toImage).paint(g);
                    g.dispose();
                    log.finest("BufferedImage painted.");
                    //File file = new File(tempStr + "tmp.jpg");
                    log.finest("New graph file "+ graphFile.getName() +" pointer created");
                    javax.imageio.ImageIO.write(graphBImage.getSubimage(0,0,100,100), "jpg", new File(Config.getProperty("BasePath") + "/test.jpg"));
                    javax.imageio.ImageIO.write(graphBImage, "jpg", graphFile /* file */);
                    log.finest("image file written.");

                    //file.renameTo(graphFile);
                } else
                {
                    throw new MraldError("BImage is NULL.  ImageConvertor failed.");
                }
            }
        } catch (IOException e)
        {
            throw new MraldError(e);
        }
        log.exiting(KEY, "createImageFile()");
    }


    /**
     *  Description of the Method
     *
     *@param  rootNode  Description of the Parameter
     */
    public static void modelLattice(LatticeNode rootNode)
    {

        ArrayList<LatticeNode> thisPoint = new ArrayList<LatticeNode>();
        thisPoint.add(rootNode);

        if (!GraphFunctions.containsVertex(rootNode.getName()))
        {
            GraphCell cell = GraphFunctions.addVertex(rootNode);
            if (rootNode.countParentNodes() == 0)
            {
                GraphFunctions.addToModel(new Object[]{cell});
            }
        }

        for (int i = 0; i < rootNode.countParentNodes(); i++)
        {

            LatticeNode parentNode = rootNode.getParents().get(i);
            if (!GraphFunctions.containsVertex(parentNode.getName()))
            {
                GraphFunctions.addVertex(parentNode);
            }

            if (!GraphFunctions.containsEdge(rootNode.getName(), parentNode.getName()))
            {
                GraphFunctions.addEdge(rootNode.getName(), parentNode.getName());
            }
            modelLattice(parentNode);
        }
    }


    /**
     *  A unit test for JUnit
     */
    /* PM: Never used!
    private static void testing()
    {

        JGraph graph = getGraph();
        GraphModel model = graph.getModel();

        graph.setPortsVisible(false);
        graph.setPortsVisible(false);
        graph.setSelectNewCells(false);
        Object[] cells = graph.getSelectionCells();
        Object parent = null;

        if (cells != null)
        {
            java.util.ArrayList children = new java.util.ArrayList();
            for (int i = 0; i < cells.length; i++)
            {
                parent = cells[i];
                while (parent != null)
                {
                    parent = model.getParent(parent);
                    System.out.println("Parent is :");
                }

                System.out.println("Cell i " + cells[i].toString());

                for (int j = 0; j < model.getChildCount(cells[i]); j++)
                {
                    children.add(model.getChild(cells[i], j));
                    System.out.println("Cell j " + model.getChild(cells[i], j).toString());
                }

            }
            Object[] childs = children.toArray();
        }
    }*/


    /**
     *  Description of the Method
     *
     *@param  xPoints  Description of the Parameter
     *@param  yPoints  Description of the Parameter
     */
    public void graphSolution(Object[] xPoints, Object[] yPoints)
    {

	log.finest("About to get modelLattice.");

        modelLattice(LatticeFunctions.getRootNode());

	log.finest("Finished with modelLattice.");

        int noOfGenes = xPoints.length;

        for (int i = (noOfGenes - 1); i > -1; i--)
        {
            int xPos = ((Integer) xPoints[i]).intValue();
            int yPos = ((Integer) yPoints[i]).intValue();

            LatticeNode node = LatticeFunctions.getNode(new Integer(i));
            positionVertex(node, xPos, yPos);
        }
        Object[] cells = graph.getRoots();


        if (cells.length > 0)
        {
            graph.getGraphLayoutCache().getMapping(cells[0], false).refresh(false);
        }

	log.finest("Creating legend.");

        createLegend();

	log.finest("Createdlegend.");

        graph.graphDidChange();
        graph.getGraphLayoutCache().reload();

        //graph.validate();
        //graph.doLayout();
        //graph.invalidate();
        //graph.revalidate();
    }


     /**
     *  Description of the Method
     *
     *@param  xPoints  Description of the Parameter
     *@param  yPoints  Description of the Parameter
     */
    public void graphSolution()
    {

        modelLattice(LatticeFunctions.getRootNode());
    Object[] cells = graph.getRoots();

    setGraphConstants(cells);
    graph.setPreferredSize(AbstractCellView.getBounds( graph.getGraphLayoutCache().getMapping(cells)).getBounds() .getSize());

        //JGraphLayoutAlgorithm.applyLayout(graph, cells, new SugiyamaLayoutAlgorithm());
        JGraphUtilities.applyLayout(graph, new SugiyamaLayoutAlgorithm());

        if (cells.length > 0)
        {
            graph.getGraphLayoutCache().getMapping(cells[0], false).refresh(false);
        }

    createLegend();

        graph.graphDidChange();
        graph.getGraphLayoutCache().reload();


    }


    /**
     *  Description of the Method
     */
    public void init()
    {
	log.finest("Graph Lattice: starting init");

        contentPane = getContentPane();

//        JTabbedPane tabbedPane = new JTabbedPane();

        addWindowListener(
            new WindowAdapter()
            {
                public void windowClosing(WindowEvent e)
                {
                    System.exit(0);
                }
            });

	log.finest("About to get customizable graph");

        graph = new CustomizableGraph(GraphFunctions.setUpGraph());
        Properties props = CustomizableGraph.LATTICE_PROPS;
        screenWidth = Integer.parseInt(props.getProperty("ScreenWidth"));
        screenHeight =  Integer.parseInt(props.getProperty("ScreenHeight"));

	log.finest("About to setSelectNewCells");

        //graph = new MraldGraph(GraphFunctions.setUpGraph());
        graph.setSelectNewCells(false);

        String[] bckGround =props.getProperty("graphBackground").split(",");
        Color bckGroundCol = new Color( Integer.parseInt(bckGround[0]),Integer.parseInt(bckGround[1]), Integer.parseInt(bckGround[2]) );

	log.finest("About to get dimensions");

        //Graphics2D g2 = (Graphics2D) g;
        java.awt.Dimension dim = new java.awt.Dimension(screenWidth, screenHeight);

        //g2.setPaint( blacktogrey);

        //g2.fill( new Rectangle(	dim) );
        graph.setBackground(bckGroundCol);

        graph.setPortsVisible(false);

        graph.setMinimumSize(dim);
        contentPane.add(graph, BorderLayout.CENTER);
        pack();
	log.finest("Finished init");
        //setVisible( true );
    }


    /*
     *  public static void main(String[] args)
     *  {
     *  GraphLattice newFrame = new GraphLattice();
     *  newFrame.pack();
     *  newFrame.setVisible( true);
     *  }
     */
    /*
     *
     */
    /**
     *  Description of the Method
     *
     *@param  root  Description of the Parameter
     *@param  x     Description of the Parameter
     *@param  y     Description of the Parameter
     */
    public void positionVertex(LatticeNode root, int x, int y)
    {
        positionVertexAt(root.getName(), x, y);
    }


    /**
     *  ALgorithm to decide optimal size of the vertex.
     *
     *@return         Description of the Return Value
     */
    private int sizeVertex()
    {
        if (LatticeFunctions.getBoxWidth() < LatticeFunctions.getWidth() / 10)
        {
            return LatticeFunctions.getBoxWidth() * 7 / 8;
        } else
        {
            return Integer.parseInt(CustomizableGraph.LATTICE_PROPS.getProperty("MaxCellWidth"));
        }

    }


     /**
     *  ALgorithm to decide optimal size of the vertex.
     *
     *@param  attr    The new graphConstants value
     *@return         Description of the Return Value
     */
    private void setGraphConstants(Object[] cells)
    {


    for (int i=0; i< cells.length; i++)
    {
            DefaultGraphCell cell = (DefaultGraphCell)cells[i];
            Map attr = cell.getAttributes();
        setGraphConstants( attr, 0, 0);
    }
    }
    /**
     *  ALgorithm to decide optimal size of the vertex.
     *
     *@param  attr    The new graphConstants value
     *@return         Description of the Return Value
     */
    private Map setGraphConstants(Map attr, int x, int y)
    {

        Properties props = CustomizableGraph.LATTICE_PROPS;
        int width = sizeVertex();
        GraphConstants.setBounds(attr, new Rectangle(x, y, width, width * 25 / 45));

        java.awt.Font font = new java.awt.Font(props.getProperty("FontName"), new Integer(props.getProperty("FontStyle")).intValue() , new Integer(props.getProperty("FontSize")).intValue());

        GraphConstants.setBorder(attr, BorderFactory.createRaisedBevelBorder());
        GraphConstants.setOpaque(attr, true);

        String[] backGroundInts =props.getProperty("Background").split(",");
        Color backGroundCol = new Color( Integer.parseInt(backGroundInts[0]),Integer.parseInt(backGroundInts[1]), Integer.parseInt(backGroundInts[2]) );

        GraphConstants.setBackground(attr, backGroundCol);
        GraphConstants.setFont(attr, font);
//        String mraldFile = "/mnt/win_d/Documents/tech/mrald_icon_small.jpg";
        //GraphConstants.setIcon( attr, new javax.swing.ImageIcon( mraldFile));
        //GraphConstants.setBackground( attr,#ccccff );

        GraphConstants.setLineStyle(attr, GraphConstants.STYLE_SPLINE);

        return attr;

    }


    /**
     *  Description of the Method
     *
     *@param  vertex  Description of the Parameter
     *@param  x       Description of the Parameter
     *@param  y       Description of the Parameter
     */
    private void positionVertexAt(Object vertex, int x, int y)
    {

        DefaultGraphCell cell = (DefaultGraphCell) GraphFunctions.getVertex(vertex.toString());
        Map attr = cell.getAttributes();
//        Rectangle2D b = GraphConstants.getBounds(attr);

        Map<DefaultGraphCell,Map> cellAttr = new HashMap<DefaultGraphCell,Map>();

        cellAttr.put(cell, setGraphConstants( attr, x, y));

    }

    private void createLegend()
    {

    // for each of the node types create a legend object
    String[] nodeTypes = Config.getLatticeFactory().getConstants().getNodeTypes();

    ArrayList<DefaultGraphCell> cells = new ArrayList<DefaultGraphCell>();
    Map<DefaultGraphCell,AttributeMap> attributes = new HashMap<DefaultGraphCell,AttributeMap>();

    int x = 0;
    int y = screenHeight - new Double(LatticeFunctions.getBoxHeight() * 0.5).intValue() ;
    int width = sizeVertex();

    if (width > 120) width = 120;

    for (int i=0; i < nodeTypes.length ; i++)
    {

         DefaultGraphCell vertex = Config.getLatticeFactory().createCell( nodeTypes[i], nodeTypes[i] );
         AttributeMap vertexAttrib = graph.getModel().createAttributes();
        //             Rectangle2D b = GraphConstants.getBounds(vertexAttrib);

         x = LatticeFunctions.getBoxWidth() * 2/3;
         if (x > 150) x = 150;
         x= x*i;
         setGraphConstants(vertexAttrib, x , y);

         GraphConstants.setBounds(vertexAttrib, new Rectangle(x, y, width * 2/3, width * 16 / 45));

         cells.add(vertex);
         attributes.put( vertex, vertexAttrib );

        }

        graph.getModel().insert( cells.toArray(), attributes, new ConnectionSet() , null, null );
    }
}

